#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>


const char* program_name;

void print_usage(FILE* stream, int exit_code)
{
    fprintf(stream, "Usage: %s options\n", program_name);
    fprintf(stream,
            "-h --help      Display this usage information.\n"
            "-s --sn        sn number.\n"
            "-m --mac       mac address.\n"
            "-d --deviceid  deviceid.\n"
            "-u --userid    userid.\n"
            );
    exit(exit_code);
}

/*从字符串的右边截取n个字符*/
char * right(char *dst,char *src, int n)
{
    char *p = src;
    char *q = dst;
    int len = strlen(src);
    if(n>len) n = len;
    p += (len-n);   /*从右边第n个字符开始*/
    while(*(q++) = *(p++));
    return dst;
}

/*从字符串的左边截取n个字符*/
char * left(char *dst,char *src, int n)
{
    char *p = src;
    char *q = dst;
    int len = strlen(src);
    if(n>len) n = len;
    /*p += (len-n);*/   /*从右边第n个字符开始*/
    while(n--) *(q++) = *(p++);
    *(q++)='\0'; /*有必要吗？很有必要*/
    return dst;
}


void ShowCerts(SSL * ssl)
{
    X509 *cert;
    char *line;
    
    cert = SSL_get_peer_certificate(ssl);
    if (cert != NULL)
    {
        printf("数字证书信息:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("证书: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("颁发者: %s\n", line);
        
        
        char buf[8192]={0};
        BIO *bio_cert = NULL;
        
        memset(buf,0,sizeof(buf));
        bio_cert = BIO_new(BIO_s_mem());
        PEM_write_bio_X509(bio_cert, cert);
        BIO_read( bio_cert, buf, 8191);
        //server证书内容
        printf("SERVER CERT:\n%s\n",buf);

	 free(line);
        X509_free(cert);
    }
    else
        printf("无证书信息！\n");
}

int main(int argc, char* argv[]){
    
    char* sn = NULL;
    char* mac_addr = NULL;
    char* deviceid = NULL;
    char* userid = NULL;
    
    
    int sockfd, len;
    struct sockaddr_in dest;
    SSL_CTX *ctx;
    SSL *ssl;

    
    char* ip = "54.199.151.2";
    //char* ip = "42.51.169.83"; //迅雷服务器
    //char* ip = "101.226.129.204"; //mp.weixin
    //char* ip = "1.83.33.164";
    //char* ip = "127.0.0.1";
    
    //char *domain = "www.renyiai.com";
    //char *domain = "kjapi.peiluyou.com";
     //char *domain = "mp.weixin.qq.com";

	char* port = "443";
    
    char password[8];
    
    int next_option;
    const char* const short_options="s:m:d:u:hv";
    
    const struct option long_options[] = {
        {"sn",      required_argument,  NULL,   's'},
        {"mac",     required_argument,  NULL,   'm'},
        {"deviceid",     required_argument,  NULL,   'd'},
        {"userid",     required_argument,  NULL,   'u'},
        {"help",    no_argument,        NULL,   'h'},
        {"verbose", no_argument,        NULL,   'v'},
        {NULL,      0,  NULL,    0}
    };
    
    int verbose = 0;
    
    program_name = argv[0];
    
    do {
        next_option = getopt_long(argc, argv, short_options, long_options, NULL);
        
        
        switch (next_option) {
            case 'h':
                print_usage(stdout, 0);
                break;
            case 's':
                if ( strlen(optarg) != 13){
                    printf("invaild sn number\n");
                    print_usage(stdout, 0);
                    abort();
                }
                sn = optarg;
                break;
            case 'm':
                if ( strlen(optarg) != 17){
                    printf("invaild mac address\n");
                    print_usage(stdout, 0);
                    abort();
                }
                mac_addr = optarg;
                break;
            case 'd':
                deviceid = optarg;
                break;
            case 'u':
                userid = optarg;
                break;
            case 'v': /* -v or --verbose */
                verbose = 1;
                break;
            case '?':
                print_usage(stderr, 1);
                break;
                
            default:
                if(!sn || !mac_addr){
                    print_usage(stdout, 0);
                }
                //abort();
                break;
        }
        
    } while (next_option != -1);
    
    if (verbose) {
        printf("sn:%s \n", sn);
        printf("mac_addr:%s \n", mac_addr);
        printf("deviceid:%s \n", deviceid);
        printf("userid:%s \n", userid);
    }
   
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx == NULL) {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    
    
    
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket");
        exit(errno);
    }
    printf("socket created\n");
    
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(port));
    if (inet_aton(ip, (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
        perror(ip);
        exit(errno);
    }
    printf("address created\n");
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }
    printf("server connected\n");
    
    
    int verify_host = 0;
    X509_VERIFY_PARAM *param;
    
    if ( verify_host )
    {
        if (!SSL_CTX_load_verify_locations(ctx, "certs/ca-certificates.crt", NULL) )
        {
            ERR_print_errors_fp(stderr);
            close(sockfd);
            return -1;
        }
        param = (X509_VERIFY_PARAM *)SSL_CTX_get0_param(ctx);
        
        X509_VERIFY_PARAM_set1_host(param, "a.peiluyou.com" , sizeof("a.peiluyou.com"));
        SSL_CTX_set_verify(ctx, 1, 0);
    }
    

    
    ssl = SSL_new(ctx);
    SSL_set_fd(ssl, sockfd);
    
    if (SSL_connect(ssl) == -1)
        ERR_print_errors_fp(stderr);
    else {
        printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
        ShowCerts(ssl);
    }
    
    
    int size = 0;
    SSL_read(ssl, &size, sizeof(size));
    printf("size is:%d\n", size);
    size = ntohl(size);
    if (size > 0x100000){
        printf("error");
    }
    
    char *data;
    data = (char*)malloc(size +1);
    memset(data, 0, size + 1);
    len = SSL_read(ssl, data, size);
    printf("接收消息成功:%s，共%d个字节的数据\n",data, len);
    
    char  sendinfo[1000] ;
    
    sprintf(sendinfo, "{\"jsonrpc\":\"2.0\",\"id\":0,\"result\":[0,{\"device_id\":\"%s\",\"device_name\":\"jj\",\"device_sn\":\"%s\",\"system_name\":\"挖矿固件\",\"system_version\":\"V1.3.186\",\"hardware_model\":\"WS1508\",\"mac_address\":\"%s\",\"lan_ip\":\"192.168.1.2\",\"account_id\":\"%s\",\"account_name\":\"%s\",\"product_id\":321,\"disk_quota\":\"10995116277760\",\"schedule_hours\":\"[ { \\\"from\\\": 0, \\\"to\\\": 24, \\\"type\\\": \\\"unlimit\\\", \\\"params\\\": { } } ]\"}]}", deviceid, sn, mac_addr, userid, userid);
    
  
    
    int len_send = strlen(sendinfo);
    len_send = htonl(len_send);
    
    len = SSL_write(ssl, &len_send, sizeof(len_send));
    if (len < 0){
        goto finish;
        //printf("消息'%d'发送失败！错误代码是%d，错误信息是'%s'\n",len_send, errno, strerror(errno));
    }
    else{
        printf("消息'%d'发送成功，共发送了%d个字节！\n",len_send, len);
    }
    
    
    len = SSL_write(ssl, sendinfo, strlen(sendinfo));
    if (len < 0){
        //printf("消息'%s'发送失败！错误代码是%d，错误信息是'%s'\n",'', errno, strerror(errno));
        goto finish;
        
    }else{
        printf("消息%s发送成功，共发送了%d个字节！\n",sendinfo, len);
    }
    
    size = 0;
    SSL_read(ssl, &size, sizeof(size));
    printf("size is:%d\n", size);
    size = ntohl(size);
    if (size > 0x100000){
        printf("error");
    }
    
    
    data = (char*)malloc(size +1);
    memset(data, 0, size + 1);
    len = SSL_read(ssl, data, size);
    if (len > 0){
        //right(password, data, 8);
        //left(password, password, 8);
        //printf("接收消息成功:'%s'，共%d个字节的数据\n",data, len);
    }else {
        //printf("消息接收失败！错误代码是%d，错误信息是'%s'\n",errno, strerror(errno));
        goto finish;
    }
    
    printf("Password: %s\n", data);
    
    goto finish;
    
    
finish:
    SSL_shutdown(ssl);
    SSL_free(ssl);
    close(sockfd);
    SSL_CTX_free(ctx);
    return 0;}
